/* image.cpp */

#include <dodo_plot/widget/image.h>
#include <dodo_plot/kernel/canvas.h>
#include <dodo_plot/kernel/plotter.h>
#include <dodo_plot/kernel/position.h>
#include <dodo_plot/kernel/basic_utils.h>
#include <cairomm/cairomm.h>

namespace dodo_plot
{

void Image::rotate_0()
{
	body_->set_rotate(Image_Body::NO_ROTATE);
}

void Image::rotate_90()
{
	body_->set_rotate(Image_Body::ROTATE_90);
}

void Image::rotate_180()
{
	body_->set_rotate(Image_Body::ROTATE_180);
}

void Image::rotate_270()
{
	body_->set_rotate(Image_Body::ROTATE_270);
}

void Image::flip_lr()
{
	//what to do?
}

void Image::flip_ud()
{
	//what to do?
}

void Image::draw(const Plotter& plotter) const
{
	body_->draw(plotter);
}

double Image::width() const
{
	return body_->width();
}

double Image::height() const
{
	return body_->height();
}

Position Image::local_pos() const
{
	return body_->local_pos();
}

Image::Image(const Image& orig)
	: body_(orig.body_)
{
	orig.body_->use_count_++;
}

Image::~Image()
{
	if(-- body_->use_count_ == 0)
		delete body_;
}

Image& Image::operator= (const Image& orig)
{
	orig.body_->use_count_++;
	if( -- body_->use_count_ == 0)
		delete body_;
	body_ = orig.body_;
	return *this;
}

// Image_Body

int Image_Body::width() const 
{ 
	if(rotate_mode_ == NO_ROTATE | 
		rotate_mode_ == ROTATE_180)
		return w_; 
	else
		return h_;
}

int Image_Body::height() const 
{ 
	if(rotate_mode_ == NO_ROTATE |
		rotate_mode_ == ROTATE_180)
		return h_; 
	else
		return w_;
}

Position Image_Body::local_pos() const 
{
	return Position(0, width(), 0, height());
}

void Image_Body::draw(const Plotter& plotter) const
{
	plotter.begin_new_path();
	Point p0 = plotter.get_abs_point(0, 0);
	Point p = plotter.get_abs_point(width(), height());
	Position pos(p0, p);
	if(pos.width() == 0 || pos.height() == 0)
		return;

	Surface_p image = Cairo::ImageSurface::create(value_,
		Cairo::FORMAT_ARGB32,
		w_, h_, stride_);

	Context_p cr = plotter.get_cr();
	cr->save();


	if(rotate_mode_ == NO_ROTATE && 
		(int)pos.width() == width() && (int)pos.height() == height()) // paint directly
	{
		cr->set_source(image, pos.left, pos.top);
	//	cr->rectangle(pos.left, pos.top, pos.width(), pos.height());
	//	cr->fill();
		cr->paint();
	}
	else
	{
		Cairo::RefPtr<Cairo::SurfacePattern> img_pat =
			Cairo::SurfacePattern::create(image);

		Cairo::Matrix scaler;
		if(rotate_mode_ == ROTATE_90 || rotate_mode_ == ROTATE_270) 
			scaler = Cairo::scaling_matrix( w_ / pos.height(),  h_ / pos.width() );
		else
			scaler = Cairo::scaling_matrix( w_ / pos.width(), h_ / pos.height() );

		img_pat->set_matrix(scaler);
		img_pat->set_filter(Cairo::Filter(filter_mode_));
		
		Surface_p scaled_image = 
			Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, pos.width(), pos.height());
		Context_p scaled_cr =
			Cairo::Context::create(scaled_image);

		switch(rotate_mode_)
		{
		case ROTATE_90:
			scaled_cr->translate(pos.width() * 0.5, pos.height() * 0.5);
			scaled_cr->rotate_degrees(90);
			scaled_cr->translate(-pos.height() * 0.5, -pos.width() * 0.5);
			break;
		case ROTATE_180:
			scaled_cr->translate(pos.width() * 0.5, pos.height() * 0.5);
			scaled_cr->rotate_degrees(180);
			scaled_cr->translate(-pos.width() * 0.5, -pos.height() * 0.5);
			break;
		case ROTATE_270:
			scaled_cr->translate(pos.width() * 0.5, pos.height() * 0.5);
			scaled_cr->rotate_degrees(270);
			scaled_cr->translate(-pos.height() * 0.5, -pos.width() * 0.5);
			break;
		default:
			;
		}


		scaled_cr->set_source(img_pat);
		scaled_cr->paint();
//		for debugging
//		scaled_image->write_to_png("raw_image.png");
		cr->set_source(scaled_image, pos.left, pos.top);
		cr->paint();
	}
	cr->restore();
	image->finish();
}

void Image_Body::push_back(const Color& c)
{
	//TODO: make the order universal...
	*p_++ = c.int_b();
	*p_++ = c.int_g();
	*p_++ = c.int_r();
	*p_++ = c.int_a();

	if(pad_byte_count_ != 0)
	{
		++current_col_;
		if(current_col_ == w_)
		{
			for(int i = 0; i < pad_byte_count_; ++i)
			{
				*p_++ = 0;
			}
			current_col_ = 0;
		}
	}
}

void Image_Body::set_rotate(Image_Body::Rotate_Mode mode)
{
	rotate_mode_ = mode;
}

void Image_Body::set_flip(Image_Body::Flip_Mode mode)
{
	//TODO: how to do?
}

Image_Body::Image_Body(int w, int h, bool is_grid_image)
	: use_count_(1), w_(w), h_(h), 
	current_col_(0),
	rotate_mode_(Image_Body::NO_ROTATE),
	flip_mode_(Image_Body::NO_FLIP)
{
	stride_ = Cairo::ImageSurface::format_stride_for_width(Cairo::FORMAT_ARGB32, w);
	pad_byte_count_ = stride_ - w_ * 4;

	value_ = new unsigned char[stride_ * h_];
	p_ = value_;

	if(is_grid_image)
		filter_mode_ = Cairo::FILTER_NEAREST;
	else
		filter_mode_ = Cairo::FILTER_BEST;
}

Image_Body::~Image_Body()
{
	delete [] value_;
}

}
